/*
 * Instituto Tecnológico de Costa Rica
 * Maestría en Ciencias de la Computación
 * Computer Graphics 2
 *
 * Proyecto
 * Ray Tracer Mejorado
 *
 * Alexander Ujueta
 */

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "../headers/RayTracer.h"
#include "../headers/cilindro.h"
#include "../headers/cono.h"
#include "../headers/cuadratica.h"
#include "../headers/disco.h"
#include "../headers/elipse.h"
#include "../headers/esfera.h"
#include "../headers/poligono.h"

void draw_scene ();

const struct def_parametro defs_parametro[] =
{
	{
			tipo: tipo_parametro_escena,
			prefijos: { "-s", "--scene" },
			num_prefijos: 2,
			requerido: 0,
			valor_default: "Escena.txt"
	}
};

color_t **buffer;
escena_t *escena;

// Pinta un pixel en las coordenadas i, j del framebuffer con el color seleccionado
void plot(int i, int j, color_t color)
{
	buffer[i][j].r = color.r;
  	buffer[i][j].g = color.g;
  	buffer[i][j].b = color.b;
  	glColor3f (buffer[i][j].r, buffer[i][j].g, buffer[i][j].b);
  	glBegin(GL_POINTS);
  	glVertex2i(i,j);
  	glEnd();
}

// Carga una imagen de un archivo avs
imagen_t* load_avs(char* ruta)
{
  FILE *archivo;
  imagen_t *imagen;
  int ancho, alto, i, j;
  int a, r, g, b;

  // Intenta abrir el archivo .avs
  if ((archivo = fopen(ruta,"r")) == NULL)
  {
     printf("No se puede abrir el archivo %s\n",ruta);
     return(NULL);
  }

  // Lee el encabezado
  fread(&ancho, sizeof(int), 1, archivo);
  ancho = FIX(ancho);
  fread(&alto, sizeof(int), 1, archivo);
  alto = FIX(alto);

  // Crea la estructura de la imagen
  imagen = (imagen_t*)malloc(sizeof(imagen_t));
  imagen->height = alto;
  imagen->width = ancho;
  imagen->texel = (color_t**)malloc(ancho * sizeof(color_t*));
  for (i = 0; i < ancho; i++)
     imagen->texel[i] = (color_t*)malloc(alto * sizeof(color_t));

  // Lee cada pixel y lo guarda en la estructura de la imagen
  for (j = alto - 1; j >= 0; j--)
     for (i = 0; i < ancho; i++)
     {
       a = fgetc(archivo);
       r = fgetc(archivo);
       g = fgetc(archivo);
       b = fgetc(archivo);
       if (a == EOF || g == EOF || r == EOF || b == EOF)
       {
          printf("Error, fin de archivo inesperado\n");
          return(NULL);
       }
       imagen->texel[i][j].r = (double)r/(double)255;
       imagen->texel[i][j].g = (double)g/(double)255;
       imagen->texel[i][j].b = (double)b/(double)255;
     }

  fclose(archivo);
  return(imagen);

}

void GuardarEscena()
{
	//Crea el archivo de imagen
	FILE *archivo = NULL;
	int i, j;

	archivo = fopen("escena.avs","w");

	if (archivo == NULL)
	{
	  printf("Error al crear el archivo de imagen\n");
	}
	else
	{
		int alto = escena->Vres;
		int ancho = escena->Hres;

		ancho = FIX(ancho);
		alto = FIX(alto);

		//Escribe el encabezado del archivo
		fwrite(&ancho,sizeof(unsigned int),1,archivo);
		fwrite(&alto,sizeof(unsigned int),1,archivo);

		alto = escena->Vres;
		ancho = escena->Hres;
		for (j = alto - 1; j >= 0; j--)
			for (i = 0; i < ancho; i++)
			{
				fputc(255, archivo);
				fputc(buffer[i][j].r * 255, archivo);
				fputc(buffer[i][j].g * 255, archivo);
				fputc(buffer[i][j].b * 255, archivo);
			}

		fclose(archivo);
		printf("Imagen guardada exitosamente\n");
	}
}

color_t ObtenerTextura(objeto_t *cosa, long double Xi, long double Yi, long double Zi, long double *Normal, imagen_t *textura)
{
	color_t color;
	int i,j;

	// Determina que tipo de objeto es
	switch(cosa->tipo)
	{
		poligono_t *poligono;
		cilindro_t *cilindro;
		esfera_t *esfera;
		long double u = 0.0, v = 0.0;

		case 4: //Polígono
			poligono = (poligono_t*)cosa->datos;
			if (cosa->texture_type == 1)
			{
				u = (((Xi - poligono->Esquina.x) * poligono->U[0]) +
					 ((Yi - poligono->Esquina.y) * poligono->U[1]) +
					 ((Zi - poligono->Esquina.z) * poligono->U[2])) / poligono->h;

				v = (((Xi - poligono->Esquina.x) * poligono->V[0]) +
					 ((Yi - poligono->Esquina.y) * poligono->V[1]) +
					 ((Zi - poligono->Esquina.z) * poligono->V[2])) / poligono->w;


			}
			else
			{
				u = (long double)((int)(((Xi - poligono->Esquina.x) * poligono->U[0]) +
					  ((Yi - poligono->Esquina.y) * poligono->U[1]) +
					  ((Zi - poligono->Esquina.z) * poligono->U[2])) % (int)cosa->TANCHO) / cosa->TANCHO;

				v = (long double)((int)(((Xi - poligono->Esquina.x) * poligono->V[0]) +
					 ((Yi - poligono->Esquina.y) * poligono->V[1]) +
					 ((Zi - poligono->Esquina.z) * poligono->V[2])) % (int)cosa->TALTO) / cosa->TALTO;
			}

			i = (int)(u * (long double)textura->width);
			j = (int)(v * (long double)textura->height);
			if (i < 0)
				i = 0;
			if (i >= textura->width)
				i = textura->width-1;
			if (j < 0)
				j = 0;
			if (j >= textura->height)
				j = textura->height-1;

			//printf("i:%i,j:%i width:%i, height:%i\n",i,j,textura->width, textura->height);
			color = textura->texel[i][j];
			break;

		case 1:
			esfera = (esfera_t*)cosa->datos;
			long double Xm,Ym,Zm;
			long double Hx,Hy,Hz;
			long double d, norma;

			if (cosa->texture_type == 1)
			{
				v = acos((Normal[0] * esfera->Nx) + (Normal[1] * esfera->Ny) + (Normal[2] * esfera->Nz)) / PI;

				d = esfera->Nx * Xi + esfera->Ny * Yi + esfera->Nz * Zi
					- (esfera->Nx * esfera->Xc + esfera->Ny * esfera->Yc + esfera->Nz * esfera->Zc);

				Xm = Xi - d * esfera->Nx;
				Ym = Yi - d * esfera->Ny;
				Zm = Zi - d * esfera->Nz;

				Hx = Xm - esfera->Xc;
				Hy = Ym - esfera->Yc;
				Hz = Zm - esfera->Zc;

				norma = sqrt(pow(Hx,2) + pow(Hy,2) + pow(Hz,2));
				Hx /= norma;
				Hy /= norma;
				Hz /= norma;



				u = (1.0 / cos((Hx * esfera->Gx) + (Hy * esfera->Gy) + (Hz * esfera->Gz))) / (2.0 * PI);
				if ((esfera->Jx * Xi + esfera->Jy * Yi + esfera->Jz * Zi + esfera->D) < 0.0)
				 u = 1.0 - u;
			}
			else if (cosa->texture_type == 2)
			{
				v = acos((Normal[0] * esfera->Nx) + (Normal[1] * esfera->Ny) + (Normal[2] * esfera->Nz)) / PI;
				v = ((int)(PI * esfera->R * v) % (int)cosa->TALTO) / cosa->TALTO;

				d = esfera->Nx * Xi + esfera->Ny * Yi + esfera->Nz * Zi
					- (esfera->Nx * esfera->Xc + esfera->Ny * esfera->Yc + esfera->Nz * esfera->Zc);

				Xm = Xi - d * esfera->Nx;
				Ym = Yi - d * esfera->Ny;
				Zm = Zi - d * esfera->Nz;

				Hx = Xm - esfera->Xc;
				Hy = Ym - esfera->Yc;
				Hz = Zm - esfera->Zc;

				norma = sqrt(pow(Hx,2) + pow(Hy,2) + pow(Hz,2));
				Hx /= norma;
				Hy /= norma;
				Hz /= norma;

				u = (1.0 / cos((Hx * esfera->Gx) + (Hy * esfera->Gy) + (Hz * esfera->Gz))) / (2.0 * PI);
				if ((esfera->Jx * Xi + esfera->Jy * Yi + esfera->Jz * Zi + esfera->D) < 0.0)
				 u = 1.0 - u;

				u = ((int)(u * 2 * PI * esfera->R) % (int)cosa->TANCHO) / cosa->TANCHO;
			}
			i = (int)(u * (long double)textura->width);
			j = (int)(v * (long double)textura->height);
			color = textura->texel[i][j];
			break;

		case 2:
			cilindro = (cilindro_t*)cosa->datos;
			if (cosa->texture_type == 1)
			{
				v = (((((Xi - cilindro->X0) * (cilindro->Xq)) +
					   ((Yi - cilindro->Y0) * (cilindro->Yq)) +
					   ((Zi - cilindro->Z0) * (cilindro->Zq)))
					   - cilindro->Back) / (cilindro->Front - cilindro->Back));
				u = ((acos((Normal[0] * cilindro->Gx) + (Normal[1] * cilindro->Gy) + (Normal[2] * cilindro->Gz))) / (2.0 * PI));
				if ((cilindro->Pa * Xi + cilindro->Pb * Yi + cilindro->Pc * Zi + cilindro->Pd) > 0.0)
					u = 1.0 - u;
			}
			else if (cosa->texture_type == 2)
			{
				v = ((long double)((int)((((Xi - cilindro->X0) * (cilindro->Xq)) +
					   ((Yi - cilindro->Y0) * (cilindro->Yq)) +
					   ((Zi - cilindro->Z0) * (cilindro->Zq)))
					   - cilindro->Back) % (int)cosa->TALTO) / (cosa->TALTO));
				u = ((acos((Normal[0] * cilindro->Gx) + (Normal[1] * cilindro->Gy) + (Normal[2] * cilindro->Gz))) / (2.0 * PI));
				if ((cilindro->Pa * Xi + cilindro->Pb * Yi + cilindro->Pc * Zi + cilindro->Pd) > 0)
					u = 1 - u;
				u = (long double)((int)(u * 2.0 * PI * cilindro->R) % (int)cosa->TANCHO) / cosa->TANCHO;
			}
			i = (int)(u * (long double)textura->width);
			j = (int)(v * (long double)textura->height);
			color = textura->texel[i][j];
			break;
  	}

	// Si tiene bump mapping calcula K1 y K2
	if (cosa->bump_index > 0)
	{
		if (i >= textura->width-1)
			escena->K1 = -((textura->texel[0][j].r - textura->texel[i-1][j].r) / 2.0);
		else if (i <= 0)
			escena->K1 = -((textura->texel[i+1][j].r - textura->texel[textura->width-1][j].r) / 2.0);
		else
			escena->K1 = -((textura->texel[i+1][j].r - textura->texel[i-1][j].r) / 2.0);

		if (j >= textura->height-1)
			escena->K2 = -((textura->texel[i][0].r - textura->texel[i][j-1].r) / 2.0);
		else if (i <= 0)
			escena->K2 = -((textura->texel[i][j+1].r - textura->texel[i][textura->height-1].r) / 2.0);
		else
			escena->K2 = -((textura->texel[i][j+1].r - textura->texel[i][j-1].r) / 2.0);
	}
  	return color;
}

long double* PerturbarNormal(objeto_t *cosa, long double *N)
{
	long double *Normal;

	switch(cosa->tipo)
  	{
  		//esfera_t *esfera;
  		//cilindro_t *cilindro;
  		poligono_t *poligono;

  		/*case 1: //Esfera
  			esfera = (esfera_t*)cosa->datos;
  			Normal = cosa->VectorNormal(Xi, Yi, Zi, esfera->Xc, esfera->Yc, esfera->Zc, esfera->R);
  			break;

  		case 2: //Cilindro
  			cilindro = (cilindro_t*)cosa->datos;
  			Normal = cosa->VectorNormal(Xi, Yi, Zi,
  					 					cilindro->X0, cilindro->Y0, cilindro->Z0,
  					 					cilindro->Xq, cilindro->Yq, cilindro->Zq,
  					 					cilindro->R);
  			break;*/

  		case 4: //Poligono
               poligono = (poligono_t*)cosa->datos;
               Normal = (long double*)malloc(3 * sizeof(long double));
               Normal[0] = N[0] + poligono->U[0] * escena->K1 + poligono->V[0] * escena->K2;
               Normal[1] = N[1] + poligono->U[1] * escena->K1 + poligono->V[1] * escena->K2;
               Normal[2] = N[2] + poligono->U[2] * escena->K1 + poligono->V[2] * escena->K2;
            break;
  	}

  	return Normal;
}

long double* CalcularNormal(objeto_t *cosa, long double Xi, long double Yi, long double Zi)
{
	long double *Normal;

	switch(cosa->tipo)
  	{
  		esfera_t *esfera;
  		cilindro_t *cilindro;
  		cono_t *cono;
  		poligono_t *poligono;
  		cuadratica_t *cuadratica;
  		disco_t *disco;
  		elipse_t *elipse;

  		case 1: //Esfera
  			esfera = (esfera_t*)cosa->datos;
  			Normal = cosa->VectorNormal(Xi, Yi, Zi, esfera->Xc, esfera->Yc, esfera->Zc, esfera->R);
  			break;

  		case 2: //Cilindro
  			cilindro = (cilindro_t*)cosa->datos;
  			Normal = cosa->VectorNormal(Xi, Yi, Zi,
  					 					cilindro->X0, cilindro->Y0, cilindro->Z0,
  					 					cilindro->Xq, cilindro->Yq, cilindro->Zq,
  					 					cilindro->R);
  			break;

  		case 3: //Cono
  			cono = (cono_t*)cosa->datos;
  			Normal = cosa->VectorNormal(Xi, Yi, Zi,
  										cono->X0, cono->Y0, cono->Z0,
  										cono->Xq, cono->Yq, cono->Zq,
  										cono->dA, cono->dB);
  			break;

  		case 4: //Poligono
               poligono = (poligono_t*)cosa->datos;
               Normal = cosa->VectorNormal(poligono->N);
            break;

        case 5: //Cuadratica
                cuadratica = (cuadratica_t*)cosa->datos;
                Normal = cosa->VectorNormal(Xi, Yi, Zi, cuadratica->A, cuadratica->B, cuadratica->C,
                                            cuadratica->D, cuadratica->E, cuadratica->F, cuadratica->G,
                                            cuadratica->H, cuadratica->J);
            break;

        case 6: //Disco
        	disco = (disco_t*)cosa->datos;
            Normal = cosa->VectorNormal(disco->N);
            break;

        case 7: //Elipse
          	elipse = (elipse_t*)cosa->datos;
            Normal = cosa->VectorNormal(elipse->N);
			break;
  	}

  	return Normal;
}

interseccion_t* Intersection(long double Xe, long double Ye, long double Ze,
							 long double Xd, long double Yd, long double Zd)
{
  interseccion_t *listaInter = NULL, *nuevaInter, *actual, *anterior;
  objeto_t *objeto = escena->objetos;
  long double distancia;

  	while (objeto != NULL)
  	{
  		distancia = 0.0;

  		//Calcula la intersección, tomando en cuenta el tipo de objeto que es
  		switch (objeto->tipo)
  		{
  			esfera_t *esfera;
  			cilindro_t *cilindro;
  			cono_t *cono;
  			poligono_t *poligono;
  			cuadratica_t *cuadratica;
  			disco_t *disco;
  			elipse_t *elipse;

  			case 1: //Esfera
  				esfera = (esfera_t*)objeto->datos;
  				distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd, esfera->Xc, esfera->Yc,
  												  esfera->Zc, esfera->R2, objeto);
  				break;

  			case 2: //Cilindro
  				cilindro = (cilindro_t*)objeto->datos;
  				distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd,
  												  cilindro->X0, cilindro->Y0, cilindro->Z0,
  												  cilindro->Xq, cilindro->Yq, cilindro->Zq,
  												  cilindro->A, cilindro->B, cilindro->C,
  												  cilindro->Back, cilindro->Front, cilindro->R2, objeto);
  				break;

  			case 3: //Cono
  				cono = (cono_t*)objeto->datos;
  				distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd,
  												  cono->X0, cono->Y0, cono->Z0,
  												  cono->Xq, cono->Yq, cono->Zq,
  												  cono->A, cono->B, cono->C,
  												  cono->Back, cono->Front, cono->dA, cono->dB, objeto);
  			 	break;

  			 case 4: //Polígono
  			 	poligono = (poligono_t*)objeto->datos;
  			 	distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd,
  			 									  poligono->A, poligono->B, poligono->C, poligono->D, poligono->Vertices, objeto);
  			 	break;

             case 5: //Cuadratica
                cuadratica = (cuadratica_t*)objeto->datos;
                distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd, cuadratica->A, cuadratica->B, cuadratica->C,
                                                    cuadratica->D, cuadratica->E, cuadratica->F, cuadratica->G,
                                                    cuadratica->H, cuadratica->J, cuadratica->K, objeto);
                break;

             case 6: //Disco
                disco = (disco_t*)objeto->datos;
                distancia =  objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd, disco->Xc, disco->Yc, disco->Zc,
  			 									  disco->A, disco->B, disco->C, disco->D, disco->RadioM, disco->Radiom, objeto);
                break;

            case 7: //Elipse
                elipse = (elipse_t*)objeto->datos;
                distancia = objeto->fIntersection(Xe, Ye, Ze, Xd, Yd, Zd, elipse->Xf1, elipse->Yf1, elipse->Zf1,
                                                    elipse->Xf2, elipse->Yf2, elipse->Zf2, elipse->A,elipse->B, elipse->C,
                                                    elipse->D, elipse->L, objeto);
                break;
		}

  		if (distancia > EPSILON)
  		{
 			nuevaInter = (interseccion_t*)malloc(sizeof(interseccion_t));
 			nuevaInter->distancia = distancia;
 			nuevaInter->objeto = objeto;

 			// Si es la primera intersección la inserta de primera en la lista
 			if (listaInter == NULL)
 			{
 				nuevaInter->siguiente = NULL;
				nuevaInter->anterior = NULL;
				listaInter = nuevaInter;
  			}
  			// Compara la nueva distancia con la primera posición de la lista
  			else if (distancia < listaInter->distancia)
  			{
	  			nuevaInter->siguiente = listaInter;
  				nuevaInter->anterior = NULL;
  				listaInter = nuevaInter;
  			}
  			// Recorre la lista para insertar la intersección ordenada
  			else
  			{
	  			anterior = listaInter;
  				actual = listaInter->siguiente;
  				while (actual!=NULL)
  				{
	  				if (distancia < actual->distancia)
  						break;
  					anterior = actual;
  					actual = actual->siguiente;
	  			}
  				nuevaInter->siguiente = actual;
  				if (actual!=NULL)
	  				actual->anterior = nuevaInter;
  				anterior->siguiente = nuevaInter;
  				nuevaInter->anterior = anterior;
  			}
  		}
  		objeto = objeto->siguiente;
  	}
  	return listaInter;
}

// Calcula el color de lo que se ve con el rayo dado
color_t CalcularColor(long double Xe, long double Ye, long double Ze, // Origen
					long double Xd, long double Yd, long double Zd, // Dirección
					int Niveles) // Nivel de espejos
{
  color_t color, colorReflejado, colorRefractado, colorAltura;
  interseccion_t *listaInter, *obstaculos, *nodo;
  imagen_t *textura;
  int i;
  objeto_t *cosa = NULL;
  luz_t *luz = escena->luces;
  long double Xi, Yi, Zi;
  long double I, P, V[3], R[3], L[3], distanciaLuz, cosNL, cosVR, cosND, Fatt, *Normal;
  long double temp;

  	listaInter = Intersection(Xe, Ye, Ze, Xd, Yd, Zd);

  	if (listaInter == NULL)
  	{
  		//Devuelve el color de background
  		color.r = escena->background.r;
  		color.g = escena->background.g;
  		color.b = escena->background.b;
  	}
  	else
  	{
  		//Calcula el punto de intersección con el objeto más cercano
  		Xi = (Xe + listaInter->distancia * Xd);
  		Yi = (Ye + listaInter->distancia * Yd);
  		Zi = (Ze + listaInter->distancia * Zd);

  		cosa = listaInter->objeto;

  		//Calcula la normal, dependiendo del tipo de objeto
  		Normal = CalcularNormal(cosa, Xi, Yi, Zi);

  		//Si tiene bump mapping calcula el nuevo punto de intersección
  		if (cosa->bump_index > 0)
  		{
  			long double altura;
  			long double *NPerturbada;

  			textura = escena->bump;
  			for(i = 1; i < cosa->bump_index; i++)
  				textura = textura->siguiente;

  			colorAltura = ObtenerTextura(cosa,Xi,Yi,Zi,Normal,textura);

  			altura = cosa->montana - cosa->valle; //Calcula la diferencia máxima de alturas

  			altura = altura * colorAltura.r; //Calcula cuanto se aleja del valle según el color

  			altura = cosa->valle + altura; //Calcula la altura basandose en el valle

  			Xi = Xi + altura * Normal[0];
  			Yi = Yi + altura * Normal[1];
  			Zi = Zi + altura * Normal[2];

  			NPerturbada = PerturbarNormal(cosa,Normal);
  			free(Normal);
  			Normal = NPerturbada;
  		}


  		//Revisa que se use la normal indicada
  		cosND = ((Normal[0] * Xd) + (Normal[1] * Yd) + (Normal[2] * Zd));
  		if (cosND > 0)
  		{
  			Normal[0] *= -1.0;
  			Normal[1] *= -1.0;
  			Normal[2] *= -1.0;
  		}
  		// Calcula la contribución de las luces
  		I = 0.0;
  		P = 0.0;
  		while (luz != NULL)
  		{
  			// Calcula y normaliza el vector que apunta a la fuente de luz
  			L[0] = (luz->Xp - Xi);
  			L[1] = (luz->Yp - Yi);
  			L[2] = (luz->Zp - Zi);

  			distanciaLuz = sqrt(pow(L[0],2) + pow(L[1],2) + pow(L[2],2));

  			L[0] = L[0] / distanciaLuz;
  			L[1] = L[1] / distanciaLuz;
  			L[2] = L[2] / distanciaLuz;

  			// Realiza los cálculos de la intensidad de la luz en ese punto
  			cosNL = ((Normal[0] * L[0]) + (Normal[1] * L[1]) + (Normal[2] * L[2]));
  			if (cosNL > 1.0)
  				cosNL = 1.0;
  			if (cosNL < 0.0)
  				cosNL = 0.0;

  			Fatt = (1.0 / (luz->C1 + (luz->C2 * distanciaLuz) + (luz->C3 * pow(distanciaLuz,2))));

  			V[0] = -Xd;
  			V[1] = -Yd;
  			V[2] = -Zd;

  			temp = ((Normal[0] * L[0]) + (Normal[1] * L[1]) + (Normal[2] * L[2]));
  			R[0] = (temp * Normal[0]);
  			R[1] = (temp * Normal[1]);
  			R[2] = (temp * Normal[2]);
  			R[0] = 2 * R[0];
  			R[1] = 2 * R[1];
  			R[2] = 2 * R[2];
  			R[0] = R[0] - L[0];
  			R[1] = R[1] - L[1];
  			R[2] = R[2] - L[2];

  			cosVR = ((V[0] * R[0]) + (V[1] * R[1]) + (V[2] * R[2]));
  			if (cosVR > 1.0)
  				cosVR = 1.0;
  			if (cosVR < 0.0)
  				cosVR = 0.0;

  			//Calcula los obstáculos que pueden haber entre la luz
  			obstaculos = Intersection(Xi, Yi, Zi, L[0], L[1], L[2]);

  			// Calcula la intensidad de la fuente de luz, tomando en cuenta la translucencia de los obstáculos
  			if ((obstaculos != NULL) && (obstaculos->distancia < distanciaLuz))
  			{
  				long double Ip2;

  				nodo = obstaculos;
  				Ip2 = luz->Ip;

				while((nodo->siguiente != NULL) && (nodo->siguiente->distancia < distanciaLuz))
					nodo = nodo->siguiente;


				while(nodo != NULL)
				{
					Ip2 *= nodo->objeto->Kt;
					nodo = nodo->anterior;
				}

				if (Ip2 < 0.0)
					Ip2 = 0.0;

				I += (cosNL * cosa->Kd * Ip2 * Fatt);
				P += (pow(cosVR,cosa->Kn) * cosa->Ks * Ip2 * Fatt);
  			}
  			else
  			{
  				I += (cosNL * cosa->Kd * luz->Ip * Fatt);
  				P += (pow(cosVR,cosa->Kn) * cosa->Ks * luz->Ip * Fatt);
  			}

  			// Libera la memoria ocupada por la lista de obstáculos
			while (obstaculos != NULL)
  			{
  				nodo = obstaculos->siguiente;
  				free(obstaculos);
  				obstaculos = nodo;
  			}

  			luz = luz->siguiente;
  		}
  		I += (escena->Ia * cosa->Ka);
  		if (I > 1.0)
  			I = 1.0;

  		// Calcula el color de ese punto según la intensidad de la luz
  		if (cosa->texture_index == -1)
  		{
  			color.r = cosa->color.r;
  			color.g = cosa->color.g;
  			color.b = cosa->color.b;
  		}
  		else
  		{
  			// Obtiene el mapa de la textura asociada
  			textura = escena->texturas;
  			for(i = 1; i < cosa->texture_index; i++)
  				textura = textura->siguiente;

  			// Obtiene el texel que corresponde al punto de intersección
  			color = ObtenerTextura(cosa, Xi, Yi, Zi, Normal, textura);
  		}

  		// Calcula el color Reflejado
  		if (Niveles != 0)
  		{
  			V[0] = -Xd;
  			V[1] = -Yd;
  			V[2] = -Zd;

  			temp = ((Normal[0] * V[0]) + (Normal[1] * V[1]) + (Normal[2] * V[2]));
  			R[0] = (temp * Normal[0]);
  			R[1] = (temp * Normal[1]);
  			R[2] = (temp * Normal[2]);
  			R[0] = 2 * R[0];
  			R[1] = 2 * R[1];
  			R[2] = 2 * R[2];
  			R[0] = R[0] - V[0];
  			R[1] = R[1] - V[1];
  			R[2] = R[2] - V[2];

  			colorReflejado = CalcularColor(Xi, Yi, Zi, R[0], R[1], R[2], Niveles-1);
  		}
  		else
  			colorReflejado = color;

  		// Calcula el color refractado
  		if (escena->TipoTransparencia == 1)
  		{
  			if (listaInter->siguiente != NULL)
				colorRefractado = CalcularColor(Xi,Yi,Zi,Xd,Yd,Zd, Niveles);
			else
				colorRefractado = escena->background;
		}

		color.r = cosa->O1 * color.r + cosa->O2 * colorReflejado.r + cosa->O3 * colorRefractado.r;
  		color.g = cosa->O1 * color.g + cosa->O2 * colorReflejado.g + cosa->O3 * colorRefractado.g;
  		color.b = cosa->O1 * color.b + cosa->O2 * colorReflejado.b + cosa->O3 * colorRefractado.b;

  		color.r = I * color.r;
  		color.g = I * color.g;
  		color.b = I * color.b;

  		// Calcula la reflexión especular en ese punto
  		color.r += (P * (1.0 - color.r));
  		color.g += (P * (1.0 - color.g));
  		color.b += (P * (1.0 - color.b));

  		//Libera la memoria que ya no se necesita
  		free(Normal);
  		while (listaInter != NULL)
  		{
  			nodo = listaInter->siguiente;
  			free(listaInter);
  			listaInter = nodo;
  		}
  	}

  	return color;
}

color_t AntiAlias(long double i, long double i2,
				  long double j, long double j2,
				  long double alfa, int nivelAntialiasing)
{
	long double Zw, Xw, Yw;
	long double Xd, Yd, Zd;
	long double L;
	color_t color1, color2, color3, color4;

	// Dispara el primer rayo
	Zw = 0.0;
	Xw = escena->x_min + (escena->x_max - escena->x_min) / (long double)escena->Hres * i;
	Yw = escena->y_min + (escena->y_max - escena->y_min) / (long double)escena->Vres * j;

	Xd = Xw - escena->Xe;
	Yd = Yw - escena->Ye;
	Zd = Zw - escena->Ze;

	L = sqrt((Xd * Xd) + (Yd * Yd) + (Zd * Zd));

	Xd = Xd/L;
	Yd = Yd/L;
	Zd = Zd/L;

	color1 = CalcularColor(escena->Xe, escena->Ye, escena->Ze, Xd, Yd, Zd, escena->NivelEspejos);

	// Dispara el segundo rayo
	Zw = 0.0;
	Xw = escena->x_min + (escena->x_max - escena->x_min) / (long double)escena->Hres * i;
	Yw = escena->y_min + (escena->y_max - escena->y_min) / (long double)escena->Vres * j2;

	Xd = Xw - escena->Xe;
	Yd = Yw - escena->Ye;
	Zd = Zw - escena->Ze;

	L = sqrt((Xd * Xd) + (Yd * Yd) + (Zd * Zd));

	Xd = Xd/L;
	Yd = Yd/L;
	Zd = Zd/L;

	color2 = CalcularColor(escena->Xe, escena->Ye, escena->Ze, Xd, Yd, Zd, escena->NivelEspejos);

	// Dispara el tercer rayo
	Zw = 0.0;
	Xw = escena->x_min + (escena->x_max - escena->x_min) / (long double)escena->Hres * i2;
	Yw = escena->y_min + (escena->y_max - escena->y_min) / (long double)escena->Vres * j;

	Xd = Xw - escena->Xe;
	Yd = Yw - escena->Ye;
	Zd = Zw - escena->Ze;

	L = sqrt((Xd * Xd) + (Yd * Yd) + (Zd * Zd));

	Xd = Xd/L;
	Yd = Yd/L;
	Zd = Zd/L;

	color3 = CalcularColor(escena->Xe, escena->Ye, escena->Ze, Xd, Yd, Zd, escena->NivelEspejos);

	// Dispara el cuarto rayo
	Zw = 0.0;
	Xw = escena->x_min + (escena->x_max - escena->x_min) / (long double)escena->Hres * i2;
	Yw = escena->y_min + (escena->y_max - escena->y_min) / (long double)escena->Vres * j2;

	Xd = Xw - escena->Xe;
	Yd = Yw - escena->Ye;
	Zd = Zw - escena->Ze;

	L = sqrt((Xd * Xd) + (Yd * Yd) + (Zd * Zd));

	Xd = Xd/L;
	Yd = Yd/L;
	Zd = Zd/L;

	color4 = CalcularColor(escena->Xe, escena->Ye, escena->Ze, Xd, Yd, Zd, escena->NivelEspejos);

	if (nivelAntialiasing > 0)
	{
		long double newAlfa = alfa / 2.0;

		color1 = AntiAlias(i, i+alfa, j, j+alfa, newAlfa, nivelAntialiasing - 1);
		color2 = AntiAlias(i+alfa, i2, j, j+alfa, newAlfa, nivelAntialiasing - 1);
		color3 = AntiAlias(i, i+alfa, j+alfa, j2, newAlfa, nivelAntialiasing - 1);
		color4 = AntiAlias(i+alfa, i2, j+alfa, j2, newAlfa, nivelAntialiasing - 1);
	}

	color_t colorResultado;

	colorResultado.r = (color1.r + color2.r + color3.r + color4.r) / 4.0;
	colorResultado.g = (color1.g + color2.g + color3.g + color4.g) / 4.0;
	colorResultado.b = (color1.b + color2.b + color3.b + color4.b) / 4.0;

	return colorResultado;
}

// Utiliza la técnica de Ray Tracing para pintar un pixel del color indicado
void RayTrace(int i, int j)
{
  long double Zw, Xw, Yw;
  long double Xd, Yd, Zd;
  long double L;
  color_t color;

  if (escena->NivelAntialiasing == 0)
  {
		Zw = 0.0;
		Xw = escena->x_min + (escena->x_max - escena->x_min) / (long double)escena->Hres * ((long double)i + 1.0/2.0);
		Yw = escena->y_min + (escena->y_max - escena->y_min) / (long double)escena->Vres * ((long double)j + 1.0/2.0);

		Xd = Xw - escena->Xe;
		Yd = Yw - escena->Ye;
		Zd = Zw - escena->Ze;

		L = sqrt((Xd * Xd) + (Yd * Yd) + (Zd * Zd));

		Xd = Xd/L;
		Yd = Yd/L;
		Zd = Zd/L;

		color = CalcularColor(escena->Xe, escena->Ye, escena->Ze, Xd, Yd, Zd, escena->NivelEspejos);
  }
  else
	  color = AntiAlias((long double)i, (long double)i+1, (long double)j, (long double)j+1, 0.5, escena->NivelAntialiasing);

  // Pinta el color obtenido
  plot(i,j,color);
}

// Función que se encarga de redibujar la pantalla
void draw_scene() {
  static int last_x = 0;
  int i, j;

  	// glClear(GL_COLOR_BUFFER_BIT);

 	// Pinta los pixels que ya están calculados
 	for (i = 0; i < last_x; i++)
    {
       	for (j = 0; j < escena->Vres; j++)
        {
        	glColor3f (buffer[i][j].r, buffer[i][j].g, buffer[i][j].b);
  			glBegin(GL_POINTS);
  			glVertex2i(i,j);
  			glEnd();
        }
    }

  	// Calcula los pixels que faltan de calcular
  	for (i = last_x; i < escena->Hres; i++)
    {
       	for (j = 0; j < escena->Vres; j++)
        {
        	RayTrace(i,j);
          	last_x = i;
        }
  	}

  	GuardarEscena();

  	glFlush();
}

// Verifica que el punto sea parte del objeto, según los planos de corte y mapas de calado asociados a él
// Regresa 1 si el punto está dentro del objeto, 0 si es de la parte cortada
int Cortar(objeto_t *objeto, long double Xi, long double Yi, long double Zi)
{
	int i, j, valido = 1;
  	plano_t *plano;


  	// Verifica con los planos de corte
  	if (objeto->nPlanos > 0)
  	{
  		for(i = 0; i < objeto->nPlanos; i++)
  		{
  			plano = escena->planos;
  			j = abs(objeto->planosCorte[i]);

  			while (j > 1)
  			{
  				plano = plano->siguiente;
  				j--;
  			}

  			if (objeto->planosCorte[i] > 0)
  			{
  				if ((plano->A * Xi + plano->B * Yi + plano->C * Zi + plano->D) <= 0.0)
  					valido = 0;
  			}
  			else
  			{
  				if ((plano->A * Xi + plano->B * Yi + plano->C * Zi + plano->D) >= 0.0)
  					valido = 0;
  			}
  		}
  	}

  	// Verifica con el mapa de calado
  	if (objeto->calado_index > 0)
  	{
		color_t color;
		imagen_t *textura;
		long double *Normal;
		int i;

		textura = escena->calado;
		for(i = 1; i < objeto->calado_index; i++)
  				textura = textura->siguiente;

  		Normal = CalcularNormal(objeto,Xi,Yi,Zi);
  		color = ObtenerTextura(objeto, Xi, Yi, Zi, Normal, textura);

  		if ((color.r != 1.0) || (color.g != 1.0) || (color.b != 1.0))
  			valido = 0;

  		free(Normal);
  	}
  	return valido;
}


int CargarEscena(char *ruta)
{
	escena = (escena_t*)malloc(sizeof(escena_t));

	FILE *archivo = NULL;
	char cadena[256];
	int i, j;

  	archivo = fopen(ruta,"r");
  	if (archivo == NULL)
  	{
  		printf("Archivo %s no encontrado\n", ruta);
  		return -1;
  	}
  	else
  	{
  		while (feof(archivo)==0)
  		{
  			fscanf(archivo,"%s\n",cadena);

  			// Definición de la escena en general
  			if (strcmp(cadena,"#SCENE") == 0)
  			{
	  			fscanf(archivo,"WIDTH %i\n",&escena->Hres);
  				fscanf(archivo,"HEIGHT %i\n",&escena->Vres);
  				fscanf(archivo,"AMBIENTLIGHT %Lf\n",&escena->Ia);
  				fscanf(archivo,"BACKGROUNDCOLOR %Lf,%Lf,%Lf\n",&escena->background.r, &escena->background.g, &escena->background.b);
  				fscanf(archivo,"XMIN %Lf\n",&escena->x_min);
  				fscanf(archivo,"XMAX %Lf\n",&escena->x_max);
  				fscanf(archivo,"YMIN %Lf\n",&escena->y_min);
  				fscanf(archivo,"YMAX %Lf\n",&escena->y_max);
  				fscanf(archivo,"EYE %Lf,%Lf,%Lf\n",&escena->Xe, &escena->Ye, &escena->Ze);
  				fscanf(archivo,"MIRROR_LEVEL %i\n",&escena->NivelEspejos);
  				fscanf(archivo,"TRANSPARENCY_LEVEL %i\n",&escena->NivelTransparencia);
  				fscanf(archivo,"TRANSPARENCY_TYPE %i\n",&escena->TipoTransparencia);
  				fscanf(archivo,"ANTIALIASING_LEVEL %i\n",&escena->NivelAntialiasing);

  				// Asigna la memoria necesaria para el framebuffer
  				buffer = (color_t **)malloc(escena->Hres * sizeof(color_t*));
  				for (i = 0; i < escena->Hres; i++)
  				{
					buffer[i] = (color_t *)malloc(escena->Vres * sizeof(color_t));
  				}

  				// Inicializa el framebuffer con el color de fondo
  				for (i = 0; i < escena->Hres; i++)
  				{
   					for (j = 0; j < escena->Vres; j++)
   					{
    					buffer[i][j].r = escena->background.r;
    					buffer[i][j].g = escena->background.g;
    					buffer[i][j].b = escena->background.b;
    				}
  				}
  			}

  			// Definición de una fuente de luz
  			else if (strcmp(cadena,"#LIGHT") == 0)
  			{
  				luz_t *luz_nueva;

  				luz_nueva = (luz_t*)malloc(sizeof(luz_t));
  				luz_nueva->siguiente = NULL;

  				fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&luz_nueva->Xp, &luz_nueva->Yp, &luz_nueva->Zp);
  				fscanf(archivo,"LIGHT_INTENSITY %Lf\n",&luz_nueva->Ip);
  				fscanf(archivo,"ATTFACTOR %Lf,%Lf,%Lf\n",&luz_nueva->C1, &luz_nueva->C2, &luz_nueva->C3);

  				// Inserta la luz nueva en la lista de luces
  				if (escena->luces != NULL)
  				{
  					luz_t *temp;
  					temp = escena->luces;
  					while(temp->siguiente != NULL)
  						temp = temp->siguiente;
  					temp->siguiente = luz_nueva;
  				}
  				else
  					escena->luces = luz_nueva;

  			}

  			// Definición de una Imagen
  			else if (strcmp(cadena,"#TEXTURE") == 0)
  			{
  				imagen_t *imagen_nueva;
  				char ruta[255];
  				fscanf(archivo,"PATH %s\n",ruta);
  				imagen_nueva = load_avs(ruta);
  				imagen_nueva->siguiente = NULL;
 				if (escena->texturas != NULL)
 				{
 					imagen_t *temp;
  					temp = escena->texturas;
  					while (temp->siguiente != NULL)
  						temp = temp->siguiente;
  					temp->siguiente = imagen_nueva;
  				}
  				else
  					escena->texturas = imagen_nueva;
  			}

  			// Definicion de un Mapa de Calado
  			else if (strcmp(cadena,"#PIERCE_MAP") == 0)
  			{
  				imagen_t *imagen_nueva;
  				char ruta[255];
  				fscanf(archivo,"PATH %s\n",ruta);
  				imagen_nueva = load_avs(ruta);
  				imagen_nueva->siguiente = NULL;
 				if (escena->calado != NULL)
 				{
 					imagen_t *temp;
  					temp = escena->calado;
  					while (temp->siguiente != NULL)
  						temp = temp->siguiente;
  					temp->siguiente = imagen_nueva;
  				}
  				else
  					escena->calado = imagen_nueva;
  			}

  			// Definicion de un Bump Map
  			else if (strcmp(cadena,"#BUMP_MAP") == 0)
  			{
  				imagen_t *imagen_nueva;
  				char ruta[255];
  				fscanf(archivo,"PATH %s\n",ruta);
  				imagen_nueva = load_avs(ruta);
  				imagen_nueva->siguiente = NULL;
 				if (escena->bump != NULL)
 				{
 					imagen_t *temp;
  					temp = escena->bump;
  					while (temp->siguiente != NULL)
  						temp = temp->siguiente;
  					temp->siguiente = imagen_nueva;
  				}
  				else
  					escena->bump = imagen_nueva;
  			}

  			// Definicion de un plano
  			else if (strcmp(cadena,"#PLANE") == 0)
  			{
  				long double X,Y,Z,Xd,Yd,Zd,norma;
  				plano_t *plano_nuevo;

  				plano_nuevo = (plano_t*)malloc(sizeof(plano_t));
  				plano_nuevo->siguiente = NULL;

  				fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&X,&Y,&Z);
  				fscanf(archivo,"DIRECTION %Lf,%Lf,%Lf\n",&Xd,&Yd,&Zd);

  				norma = sqrt(pow(Xd,2) + pow(Yd,2) + pow(Zd,2));

  				plano_nuevo->A = Xd/norma;
  				plano_nuevo->B = Yd/norma;
  				plano_nuevo->C = Zd/norma;
  				plano_nuevo->D = -(plano_nuevo->A * X + plano_nuevo->B * Y + plano_nuevo->C * Z);

  				if (escena->planos != NULL)
  				{
  					plano_t *temp;
  					temp = escena->planos;
  					while(temp->siguiente != NULL)
  						temp = temp->siguiente;
  					temp->siguiente = plano_nuevo;
  				}
  				else
  					escena->planos = plano_nuevo;
  			}

  			// Definición de una Esfera
  			else if ((strcmp(cadena,"#SPHERE") == 0) || (strcmp(cadena,"#TEXTURED_SPHERE") == 0))
  			{
  				objeto_t *objeto_nuevo;
  				esfera_t *datos_esfera;
  				long double norma;

  				objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
  				objeto_nuevo->siguiente = NULL;

  				// Crea la estrucutra para los datos de la esfera
  				// y define el objeto nuevo como una esfera (tipo 1)
  				datos_esfera = (esfera_t*)malloc(sizeof(esfera_t));
  				objeto_nuevo->tipo = 1;
  				objeto_nuevo->fIntersection = IntEsfera;
  				objeto_nuevo->VectorNormal = NormalEsfera;
  				objeto_nuevo->datos = datos_esfera;

  				fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&datos_esfera->Xc, &datos_esfera->Yc, &datos_esfera->Zc);
  				fscanf(archivo,"RADIUS %Lf\n",&datos_esfera->R);
  				datos_esfera->R2 = pow(datos_esfera->R,2);
  				fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);

  				// Si tiene una textura pide los datos necesarios
  				if (strcmp(cadena,"#TEXTURED_SPHERE") == 0)
  				{
  					fscanf(archivo,"TEXTURE %i\n",&objeto_nuevo->texture_index);
  					fscanf(archivo,"TEXTURE_TYPE %i\n",&objeto_nuevo->texture_type);

  					// Si no es stretched es tiled y tiene que definir TALTO y TANCHO
  					if (objeto_nuevo->texture_type != 1)
  					{
  						fscanf(archivo,"T_WIDTH %Lf\n",&objeto_nuevo->TANCHO);
  						fscanf(archivo,"T_HEIGHT %Lf\n",&objeto_nuevo->TALTO);
  					}
  				}
  				else
  				{
  					objeto_nuevo->texture_index = -1;
  					objeto_nuevo->texture_type = 1;
  				}


  				fscanf(archivo,"NORTH %Lf, %Lf, %Lf\n",&datos_esfera->Nx, &datos_esfera->Ny, &datos_esfera->Nz);
  				fscanf(archivo,"GREENWICH %Lf, %Lf, %Lf\n",&datos_esfera->Gx, &datos_esfera->Gy, &datos_esfera->Gz);

  				datos_esfera->Jx = (datos_esfera->Gy * datos_esfera->Nz - datos_esfera->Ny * datos_esfera->Gz);
  				datos_esfera->Jy = (datos_esfera->Nx * datos_esfera->Gz - datos_esfera->Gx * datos_esfera->Nz);
  				datos_esfera->Jz = (datos_esfera->Gx * datos_esfera->Ny - datos_esfera->Nx * datos_esfera->Gy);

  				datos_esfera->Gx = (datos_esfera->Jy * datos_esfera->Nz - datos_esfera->Ny * datos_esfera->Jz);
  				datos_esfera->Gy = (datos_esfera->Nx * datos_esfera->Jz - datos_esfera->Jx * datos_esfera->Nz);
  				datos_esfera->Gz = (datos_esfera->Jx * datos_esfera->Ny - datos_esfera->Nx * datos_esfera->Jy);

  				norma = sqrt(pow(datos_esfera->Nx,2) + pow(datos_esfera->Ny,2) + pow(datos_esfera->Nz,2));
  				datos_esfera->Nx /= norma;
  				datos_esfera->Ny /= norma;
  				datos_esfera->Nz /= norma;

  				norma = sqrt(pow(datos_esfera->Gx,2) + pow(datos_esfera->Gy,2) + pow(datos_esfera->Gz,2));
  				datos_esfera->Gx /= norma;
  				datos_esfera->Gy /= norma;
  				datos_esfera->Gz /= norma;

  				norma = sqrt(pow(datos_esfera->Jx,2) + pow(datos_esfera->Jy,2) + pow(datos_esfera->Jz,2));
  				datos_esfera->Jx /= norma;
  				datos_esfera->Jy /= norma;
  				datos_esfera->Jz /= norma;

  				datos_esfera->D = -(datos_esfera->Jx* datos_esfera->Xc + datos_esfera->Jy * datos_esfera->Yc + datos_esfera->Jz * datos_esfera->Zc);

  				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

  				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

				// Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}


  			else if ((strcmp(cadena,"#CYLINDER") == 0) || (strcmp(cadena,"#TEXTURED_CYLINDER") == 0))
  			{
  				objeto_t *objeto_nuevo;
  				cilindro_t *datos_cilindro;
  				long double N;

  				objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
  				objeto_nuevo->siguiente = NULL;

  				// Crea la estrucutra para los datos del cilindro
  				// y define el objeto nuevo como un cilindro (tipo 2)
  				datos_cilindro = (cilindro_t*)malloc(sizeof(cilindro_t));
  				objeto_nuevo->tipo = 2;
  				objeto_nuevo->fIntersection = IntCilindro;
  				objeto_nuevo->VectorNormal = NormalCilindro;
  				objeto_nuevo->datos = datos_cilindro;

  				fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&datos_cilindro->X0, &datos_cilindro->Y0, &datos_cilindro->Z0);
  				fscanf(archivo,"RADIUS %Lf\n",&datos_cilindro->R);
  				datos_cilindro->R2 = pow(datos_cilindro->R,2);
  				fscanf(archivo,"DIRECTION %Lf,%Lf,%Lf\n",&datos_cilindro->Xq, &datos_cilindro->Yq, &datos_cilindro->Zq);
  				N = sqrt(pow(datos_cilindro->Xq,2) + pow(datos_cilindro->Yq,2) + pow(datos_cilindro->Zq,2));
  				datos_cilindro->Xq /= N;
  				datos_cilindro->Yq /= N;
  				datos_cilindro->Zq /= N;
  				datos_cilindro->A = datos_cilindro->Xq * datos_cilindro->Yq;
  				datos_cilindro->B = datos_cilindro->Xq * datos_cilindro->Zq;
  				datos_cilindro->C = datos_cilindro->Yq * datos_cilindro->Zq;
  				fscanf(archivo,"BACK %Lf\n",&datos_cilindro->Back);
  				fscanf(archivo,"FRONT %Lf\n",&datos_cilindro->Front);
  				fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);

  				// Si tiene una textura pide los datos necesarios
  				if (strcmp(cadena,"#TEXTURED_CYLINDER") == 0)
  				{
  					fscanf(archivo,"TEXTURE %i\n",&objeto_nuevo->texture_index);
  					fscanf(archivo,"TEXTURE_TYPE %i\n",&objeto_nuevo->texture_type);
  					fscanf(archivo,"GREENWICH %Lf,%Lf,%Lf\n",&datos_cilindro->Gx, &datos_cilindro->Gy, &datos_cilindro->Gz);

  					// Si no es stretched es tiled y tiene que definir TALTO y TANCHO
  					if (objeto_nuevo->texture_type != 1)
  					{
  						fscanf(archivo,"T_WIDTH %Lf\n",&objeto_nuevo->TANCHO);
  						fscanf(archivo,"T_HEIGHT %Lf\n",&objeto_nuevo->TALTO);
  					}

  					datos_cilindro->Pa = (datos_cilindro->Gy * datos_cilindro->Zq - datos_cilindro->Yq * datos_cilindro->Gz);
  					datos_cilindro->Pb = (datos_cilindro->Xq * datos_cilindro->Gz - datos_cilindro->Gx * datos_cilindro->Zq);
  					datos_cilindro->Pc = (datos_cilindro->Gx * datos_cilindro->Yq - datos_cilindro->Xq * datos_cilindro->Gy);
  					datos_cilindro->Pd = -(datos_cilindro->Pa * datos_cilindro->X0 + datos_cilindro->Pb * datos_cilindro->Y0 + datos_cilindro->Pc * datos_cilindro->Z0);
  				}
  				else
  				{
  					objeto_nuevo->texture_index = -1;
  					objeto_nuevo->texture_type = 1;
  				}

  				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

  				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

				// Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}


  			else if ((strcmp(cadena,"#CONE") == 0) || (strcmp(cadena,"#TEXTURED_CONE") == 0))
  			{
  				objeto_t *objeto_nuevo;
  				cono_t *datos_cono;
  				long double N;

  				objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
  				objeto_nuevo->siguiente = NULL;

  				// Crea la estrucutra para los datos del cono
  				// y define el objeto nuevo como un cono (tipo 3)
  				datos_cono = (cono_t*)malloc(sizeof(cono_t));
  				objeto_nuevo->tipo = 3;
  				objeto_nuevo->fIntersection = IntCono;
  				objeto_nuevo->VectorNormal = NormalCono;
  				objeto_nuevo->datos = datos_cono;

  				fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&datos_cono->X0, &datos_cono->Y0, &datos_cono->Z0);
  				fscanf(archivo,"OPENING %Lf,%Lf\n",&datos_cono->dA, &datos_cono->dB);
  				fscanf(archivo,"DIRECTION %Lf,%Lf,%Lf\n",&datos_cono->Xq, &datos_cono->Yq, &datos_cono->Zq);
  				N = sqrt(pow(datos_cono->Xq,2) + pow(datos_cono->Yq,2) + pow(datos_cono->Zq,2));
  				datos_cono->Xq /= N;
  				datos_cono->Yq /= N;
  				datos_cono->Zq /= N;
  				datos_cono->A = datos_cono->Xq * datos_cono->Yq;
  				datos_cono->B = datos_cono->Xq * datos_cono->Zq;
  				datos_cono->C = datos_cono->Yq * datos_cono->Zq;
  				fscanf(archivo,"BACK %Lf\n",&datos_cono->Back);
  				fscanf(archivo,"FRONT %Lf\n",&datos_cono->Front);
  				fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);

  				// Si tiene una textura pide los datos necesarios
  				if (strcmp(cadena,"#TEXTURED_CONE") == 0)
  				{
  					fscanf(archivo,"TEXTURE %i\n",&objeto_nuevo->texture_index);
  					fscanf(archivo,"TEXTURE_TYPE %i\n",&objeto_nuevo->texture_type);

  					// Si no es stretched es tiled y tiene que definir TALTO y TANCHO
  					if (objeto_nuevo->texture_type != 1)
  					{
  						fscanf(archivo,"T_WIDTH %Lf\n",&objeto_nuevo->TANCHO);
  						fscanf(archivo,"T_HEIGHT %Lf\n",&objeto_nuevo->TALTO);
  					}
  				}
  				else
  				{
  					objeto_nuevo->texture_index = -1;
  					objeto_nuevo->texture_type = 1;
  				}

  				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

  				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

				// Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}


  			else if ((strcmp(cadena,"#POLYGON") == 0) || (strcmp(cadena,"#TEXTURED_POLYGON") == 0))
  			{
                objeto_t *objeto_nuevo;
                poligono_t *datos_poligono;
                vertice_t *vertice_Actual, *temp3;
                vertice_t *abajo, *arriba, *derecha, *izquierda, *adentro, *afuera;
                vertice_t abajo_izquierda, abajo_derecha, arriba_izquierda;
                int cont;
                long double Norma;

                objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
                objeto_nuevo->siguiente = NULL;

  				 //Crea la estructura para los datos del poligono
  				 //y define el objeto nuevo como un poligono (tipo 4)
                datos_poligono = (poligono_t*)malloc(sizeof(poligono_t));
                objeto_nuevo->tipo = 4;
                objeto_nuevo->fIntersection = IntPoligono;
  				objeto_nuevo->VectorNormal = NormalPoligono;
                objeto_nuevo->datos = datos_poligono;

                fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);
  				if (escena->TipoTransparencia != 1)
  					fscanf(archivo,"REFRACTION_INDEX %Lf\n",&objeto_nuevo->Ni);

  				// Si tiene una textura pide los datos necesarios
  				if (strcmp(cadena,"#TEXTURED_POLYGON") == 0)
  				{
  					fscanf(archivo,"TEXTURE %i\n",&objeto_nuevo->texture_index);
  					fscanf(archivo,"TEXTURE_TYPE %i\n",&objeto_nuevo->texture_type);

  					// Si no es stretched es tiled y tiene que definir TALTO y TANCHO
  					if (objeto_nuevo->texture_type != 1)
  					{
  						fscanf(archivo,"T_WIDTH %Lf\n",&objeto_nuevo->TANCHO);
  						fscanf(archivo,"T_HEIGHT %Lf\n",&objeto_nuevo->TALTO);
  					}
  				}
  				else
  				{
  					objeto_nuevo->texture_index = -1;
  					objeto_nuevo->texture_type = 1;
  				}

  				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}

  				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

  				fscanf(archivo,"POINTS %Lf\n", &datos_poligono->NPuntos);
  				temp3 = NULL;
                for(cont = 0; cont < datos_poligono->NPuntos; cont++)
                {
                    vertice_Actual = (vertice_t*)malloc(sizeof(vertice_t));
                    fscanf(archivo,"%Lf,%Lf,%Lf\n",&vertice_Actual->x,&vertice_Actual->y,&vertice_Actual->z);
                    vertice_Actual->siguiente = temp3;
                    temp3 = vertice_Actual;
                }
                vertice_Actual = temp3;
                datos_poligono->Vertices = vertice_Actual;

                //calcular A, B, C, D.. (de rebote sale la normal del plano)

                //los tres puntos de referencia, los vectores de trabajo y la Norma de la Normal
                vertice_t P0, P1, P2;
                long double V1[3], V2[3], Normal[3];

                // Obtiene los tres primeros puntos del poligono
                vertice_t *curr = datos_poligono->Vertices;
                P0.x = curr->x;
                P0.y = curr->y;
                P0.z = curr->z;
                curr = curr->siguiente;

                P1.x = curr->x;
                P1.y = curr->y;
                P1.z = curr->z;
                curr = curr->siguiente;

                P2.x = curr->x;
                P2.y = curr->y;
                P2.z = curr->z;

                // Calcula los vectores entre un punto y los otros dos
                V1[0] = P1.x - P0.x;
                V1[1] = P1.y - P0.y;
                V1[2] = P1.z - P0.z;

                V2[0] = P2.x - P0.x;
                V2[1] = P2.y - P0.y;
                V2[2] = P2.z - P0.z;

                // Calcula la normal del plano
                Normal[0] = V1[1]*V2[2]-V2[1]*V1[2];
                Normal[1] = V2[0]*V1[2]-V1[0]*V2[2];
                Normal[2] = V1[0]*V2[1]-V2[0]*V1[1];
                Norma = sqrt(pow(Normal[0],2)+pow(Normal[1],2)+pow(Normal[2],2));
    			Normal[0] = Normal[0]/Norma;
    			Normal[1] = Normal[1]/Norma;
    			Normal[2] = Normal[2]/Norma;

                datos_poligono->N[0] = Normal[0];
                datos_poligono->N[1] = Normal[1];
                datos_poligono->N[2] = Normal[2];
                datos_poligono->A = Normal[0];
                datos_poligono->B = Normal[1];
                datos_poligono->C = Normal[2];
                datos_poligono->D = -Normal[0]*P0.x - Normal[1]*P0.y - Normal[2]*P0.z;

                //Proyecta el plano, calcula las coordenadas u, v
                int max;

                if ((fabs(datos_poligono->A) >= fabs(datos_poligono->B)) && (fabs(datos_poligono->A) >= fabs(datos_poligono->C)))
                	max = 1;
                else if ((fabs(datos_poligono->B) >= fabs(datos_poligono->A)) && (fabs(datos_poligono->B) >= fabs(datos_poligono->C)))
                	max = 2;
                else
                	max = 3;

                vertice_Actual = datos_poligono->Vertices;
                while(vertice_Actual != NULL)
                {
                	switch(max)
                	{
                		case 1:
                			vertice_Actual->u = vertice_Actual->y;
                			vertice_Actual->v = vertice_Actual->z;
                			break;
                		case 2:
                			vertice_Actual->u = vertice_Actual->x;
                			vertice_Actual->v = vertice_Actual->z;
                			break;
                		case 3:
                			vertice_Actual->u = vertice_Actual->x;
                			vertice_Actual->v = vertice_Actual->y;
                			break;
                	}
                	vertice_Actual = vertice_Actual->siguiente;
                }

                // Calcula un rectángulo alrededor del polígono para poder aplicarle texturas
                vertice_Actual = datos_poligono->Vertices;
                if (vertice_Actual != NULL)
                	abajo = arriba = derecha = izquierda = adentro = afuera = vertice_Actual;
                while(vertice_Actual != NULL)
                {
                	if (vertice_Actual->x < izquierda->x)
                		izquierda = vertice_Actual; //Calcula el x menor
                	if (vertice_Actual->x > derecha->x)
                		derecha = vertice_Actual; //Calcula el x mayor
                	if (vertice_Actual->y < abajo->y)
                		abajo = vertice_Actual; //Calcula el y menor
                	if (vertice_Actual->y > arriba->y)
                		arriba = vertice_Actual; //Calcula el y mayor
                	if (vertice_Actual->z < adentro->z)
                		afuera = vertice_Actual; //Calcula el z menor
                	if (vertice_Actual->z > afuera->z)
                		adentro = vertice_Actual; //Calcula el z mayor

                	vertice_Actual = vertice_Actual->siguiente;
                }

                if (max == 1)
				{
                	abajo_izquierda.x = izquierda->x;
                	abajo_izquierda.y = abajo->y;
                	abajo_izquierda.z = adentro->z;

                	abajo_derecha.x = derecha->x;
                	abajo_derecha.y = abajo->y;
                	abajo_derecha.z = afuera->z;

                	arriba_izquierda.x = izquierda->x;
                	arriba_izquierda.y = arriba->y;
                	arriba_izquierda.z = adentro->z;
				}
				else if (max == 2)
				{
					abajo_izquierda.x = izquierda->x;
                	abajo_izquierda.y = abajo->y;
                	abajo_izquierda.z = afuera->z;

                	abajo_derecha.x = derecha->x;
                	abajo_derecha.y = abajo->y;
                	abajo_derecha.z = afuera->z;

                	arriba_izquierda.x = izquierda->x;
                	arriba_izquierda.y = arriba->y;
                	arriba_izquierda.z = adentro->z;
				}
				else if (max == 3)
				{
					abajo_izquierda.x = izquierda->x;
                	abajo_izquierda.y = abajo->y;
                	abajo_izquierda.z = afuera->z;

                	abajo_derecha.x = derecha->x;
                	abajo_derecha.y = abajo->y;
                	abajo_derecha.z = adentro->z;

                	arriba_izquierda.x = izquierda->x;
                	arriba_izquierda.y = arriba->y;
                	arriba_izquierda.z = afuera->z;
				}

                datos_poligono->Esquina = abajo_izquierda;

                // Calcula h y w
                datos_poligono->h = sqrt(pow((abajo_derecha.x - abajo_izquierda.x),2) +
                						 pow((abajo_derecha.y - abajo_izquierda.y),2) +
                						 pow((abajo_derecha.z - abajo_izquierda.z),2));

                datos_poligono->w = sqrt(pow((arriba_izquierda.x - abajo_izquierda.x),2) +
                						 pow((arriba_izquierda.y - abajo_izquierda.y),2) +
                						 pow((arriba_izquierda.z - abajo_izquierda.z),2));

                // Calcula los vectores U y V
                datos_poligono->U[0] = (abajo_derecha.x - abajo_izquierda.x) / datos_poligono->h;
                datos_poligono->U[1] = (abajo_derecha.y - abajo_izquierda.y) / datos_poligono->h;
                datos_poligono->U[2] = (abajo_derecha.z - abajo_izquierda.z) / datos_poligono->h;

				datos_poligono->V[0] = (arriba_izquierda.x - abajo_izquierda.x) / datos_poligono->w;
                datos_poligono->V[1] = (arriba_izquierda.y - abajo_izquierda.y) / datos_poligono->w;
                datos_poligono->V[2] = (arriba_izquierda.z - abajo_izquierda.z) / datos_poligono->w;

                // Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}

  			else if (strcmp(cadena,"#CUADRATIC") == 0)
  			{
  			    objeto_t *objeto_nuevo;
                cuadratica_t *datos_cuadratica;

                objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
                objeto_nuevo->siguiente = NULL;

                // Crea la estrucutra para los datos de la cuadratica
  				// y define el objeto nuevo como un cono (tipo 7)
  				datos_cuadratica = (cuadratica_t*)malloc(sizeof(cuadratica_t));
  				objeto_nuevo->tipo = 5;
  				objeto_nuevo->fIntersection = IntCuadratica;
  				objeto_nuevo->VectorNormal = NormalCuadratica;
  				objeto_nuevo->datos = datos_cuadratica;

  				fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);
  				if (escena->TipoTransparencia != 1)
  					fscanf(archivo,"REFRACTION_INDEX %Lf\n",&objeto_nuevo->Ni);

  				//cuadratica sin texturas
                objeto_nuevo->texture_index = -1;
                //coeficientes de la ecuacion cartesiana
                fscanf(archivo,"A %Lf\n",&datos_cuadratica->A);
                fscanf(archivo,"B %Lf\n",&datos_cuadratica->B);
                fscanf(archivo,"C %Lf\n",&datos_cuadratica->C);
                fscanf(archivo,"D %Lf\n",&datos_cuadratica->D);
                fscanf(archivo,"E %Lf\n",&datos_cuadratica->E);
                fscanf(archivo,"F %Lf\n",&datos_cuadratica->F);
                fscanf(archivo,"G %Lf\n",&datos_cuadratica->G);
                fscanf(archivo,"H %Lf\n",&datos_cuadratica->H);
                fscanf(archivo,"J %Lf\n",&datos_cuadratica->J);
                fscanf(archivo,"K %Lf\n",&datos_cuadratica->K);

                // Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

                // Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}

  			else if (strcmp(cadena,"#DISC") == 0)
  			{
  			    objeto_t *objeto_nuevo;
                disco_t *datos_disco;

                objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
                objeto_nuevo->siguiente = NULL;

                //Crea la estructura para los datos del disco
  				 //y define el objeto nuevo como un disco (tipo 5)
                datos_disco = (disco_t*)malloc(sizeof(disco_t));
                objeto_nuevo->tipo = 6;
                objeto_nuevo->fIntersection = IntDisco;
  				objeto_nuevo->VectorNormal = NormalDisco;
                objeto_nuevo->datos = datos_disco;

                fscanf(archivo,"POINT %Lf,%Lf,%Lf\n",&datos_disco->Xc,&datos_disco->Yc,&datos_disco->Zc);
  				fscanf(archivo,"RADIUSM %Lf\n",&datos_disco->RadioM);
  				fscanf(archivo,"RADIUSm %Lf\n",&datos_disco->Radiom);
  				fscanf(archivo,"NORMAL %Lf,%Lf,%Lf\n",&datos_disco->N[0],&datos_disco->N[1],&datos_disco->N[2]);
                fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);
  				if (escena->TipoTransparencia != 1)
  					fscanf(archivo,"REFRACTION_INDEX %Lf\n",&objeto_nuevo->Ni);

                //disco sin texturas
                objeto_nuevo->texture_index = -1;

                //Calcula el Plano del Disco

                datos_disco->A = datos_disco->N[0];
                datos_disco->B = datos_disco->N[1];
                datos_disco->C = datos_disco->N[2];
                datos_disco->D = -(datos_disco->A*datos_disco->Xc + datos_disco->B*datos_disco->Yc + datos_disco->C*datos_disco->Zc);

				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

  				// Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}

  			else if (strcmp(cadena,"#ELLIPSE") == 0)
  			{
  			    objeto_t *objeto_nuevo;
                elipse_t *datos_elipse;

                objeto_nuevo = (objeto_t*)malloc(sizeof(objeto_t));
                objeto_nuevo->siguiente = NULL;

                //Crea la estructura para los datos del disco
  				 //y define el objeto nuevo como un disco (tipo 5)
                datos_elipse = (elipse_t*)malloc(sizeof(elipse_t));
                objeto_nuevo->tipo = 7;
                objeto_nuevo->fIntersection = IntElipse;
  				objeto_nuevo->VectorNormal = NormalElipse;
                objeto_nuevo->datos = datos_elipse;

                fscanf(archivo,"F1 %Lf,%Lf,%Lf\n",&datos_elipse->Xf1,&datos_elipse->Yf1,&datos_elipse->Zf1);
                fscanf(archivo,"F2 %Lf,%Lf,%Lf\n",&datos_elipse->Xf2,&datos_elipse->Yf2,&datos_elipse->Zf2);
                fscanf(archivo,"L %Lf\n",&datos_elipse->L);
                fscanf(archivo,"NORMAL %Lf,%Lf,%Lf\n",&datos_elipse->N[0],&datos_elipse->N[1],&datos_elipse->N[2]);
                fscanf(archivo,"COLOR %Lf,%Lf,%Lf\n",&objeto_nuevo->color.r, &objeto_nuevo->color.g, &objeto_nuevo->color.b);
  				fscanf(archivo,"DIFFUSE_LIGHTING %Lf\n",&objeto_nuevo->Kd);
  				fscanf(archivo,"SPECULAR_REFLECTION %Lf\n",&objeto_nuevo->Ks);
  				fscanf(archivo,"REFLECTION_AREA %Lf\n",&objeto_nuevo->Kn);
  				fscanf(archivo,"ABS_SHADOW %Lf\n",&objeto_nuevo->Ka);
  				fscanf(archivo,"MIRROR_REFLECTION %Lf\n",&objeto_nuevo->O2);
  				fscanf(archivo,"TRANSPARENCY %Lf\n",&objeto_nuevo->O3);
  				fscanf(archivo,"TRANSLUCENCY %Lf\n",&objeto_nuevo->Kt);
  				if (escena->TipoTransparencia != 1)
  					fscanf(archivo,"REFRACTION_INDEX %Lf\n",&objeto_nuevo->Ni);

  				//elipse sin texturas
                objeto_nuevo->texture_index = -1;

  				//Calcula el Plano del Elipse

                datos_elipse->A = datos_elipse->N[0];
                datos_elipse->B = datos_elipse->N[1];
                datos_elipse->C = datos_elipse->N[2];
                datos_elipse->D = -datos_elipse->A*datos_elipse->Xf1 - datos_elipse->B*datos_elipse->Yf1 - datos_elipse->C*datos_elipse->Zf1;

				// Obtiene los planos de corte que usa el objeto
  				int n, index;

  				fscanf(archivo,"PLANES %i ",&n);
  				objeto_nuevo->nPlanos = n;
  				if (n != 0)
  					objeto_nuevo->planosCorte = (long double*)malloc(n * sizeof(long double));
  				else
  					objeto_nuevo->planosCorte = NULL;

  				while (n != 0)
  				{
  					fscanf(archivo,"%i",&index);
  					n--;
  					objeto_nuevo->planosCorte[n] = index;
  				}
  				fscanf(archivo,"\n");

  				//Lee el mapa de calado asociado a la textura, debe ser mayor a 0
  				fscanf(archivo,"PIERCE_MAP %i\n",&objeto_nuevo->calado_index);

  				//Lee la información para Bump Mapping
  				/*fscanf(archivo,"BUMP_MAP %i\n",&objeto_nuevo->bump_index);
  				if (objeto_nuevo->bump_index >= 0)
  				{
  					fscanf(archivo,"RANGE %Lf,%Lf\n",&objeto_nuevo->valle, &objeto_nuevo->montana);
  				}*/

				objeto_nuevo->O1 = 1.0 - objeto_nuevo->O2 - objeto_nuevo->O3;

                // Inserta el objeto nuevo en la lista de objetos
				if (escena->objetos != NULL)
				{
					objeto_t *temp;
					temp = escena->objetos;
					while(temp->siguiente != NULL)
						temp = temp->siguiente;
					temp->siguiente = objeto_nuevo;
				}
				else
					escena->objetos = objeto_nuevo;
  			}
		}

  		return 0;
  	}
}

// Libera la memoria que ocupan los objetos y las luces
void LiberarMemoria()
{
	objeto_t *objeto;
	luz_t *luz;
	vertice_t *vertice;
	poligono_t *poligono;
	imagen_t *imagen;

	while (escena->objetos != NULL)
	{
		objeto = escena->objetos->siguiente;
		switch(escena->objetos->tipo)
		{
			case 1:
				free((esfera_t*)escena->objetos->datos);
				break;
			case 2:
				free((cilindro_t*)escena->objetos->datos);
				break;
			case 3:
				free((cono_t*)escena->objetos->datos);
				break;
			case 4:
				poligono = (poligono_t*)escena->objetos->datos;
				while (poligono->Vertices != NULL)
				{
					vertice = poligono->Vertices->siguiente;
					free(poligono->Vertices);
					poligono->Vertices = vertice;
				}
				free(poligono);
				break;
		}
		free(escena->objetos);
		escena->objetos = objeto;
	}

	while (escena->texturas != NULL)
	{
		imagen = escena->texturas->siguiente;
		free(escena->texturas);
		escena->texturas = imagen;
	}

	while (escena->calado != NULL)
	{
		imagen = escena->calado->siguiente;
		free(escena->calado);
		escena->calado = imagen;
	}

	while (escena->bump != NULL)
	{
		imagen = escena->bump->siguiente;
		free(escena->bump);
		escena->bump = imagen;
	}

	while (escena->luces != NULL)
	{
		luz = escena->luces->siguiente;
		free(escena->luces);
		escena->luces = luz;
	}
}

// Función principal del programa, inicializa glut
int main(int argc, char** argv)
{
	int i = 0;
	int j = 0;
	int k = 0;
	struct parametro parametros[dimensionof(defs_parametro)];

	for (i = 1; i < argc - 1; i++)
	{
		for (j = 0; j < dimensionof(defs_parametro); j++ )
		{
			for (k = 0; k < defs_parametro[j].num_prefijos; k++)
			{
				if (strcmp(argv[i], defs_parametro[j].prefijos[k]) == 0)
				{
					parametros[j].definicion = &defs_parametro[j];
					parametros[j].definido = 1;
					i++;
					k = defs_parametro[j].num_prefijos;
					j = dimensionof(defs_parametro);
					break;
				}
			}
		}
	}

	for (j = 0; j < dimensionof(defs_parametro); j++ )
	{
		if(parametros[j].definido != 1)
		{
			if (defs_parametro[j].requerido)
			{
				printf("usage: RayTracer -s [scene definition path] ");
				return 0;
			}
			else
				parametros[j].valor = defs_parametro[j].valor_default;
		}
	}

	//Carga la escena en memoria
	char* ruta_escena = (char*)parametros[tipo_parametro_escena].valor;
	CargarEscena(ruta_escena);

  	glutInit(&argc, argv);
  	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  	glutInitWindowSize(escena->Hres,escena->Vres);
  	glutCreateWindow("Ray Tracer");
  	glClear(GL_COLOR_BUFFER_BIT);
  	gluOrtho2D(-0.5, escena->Hres-0.5, -0.5, escena->Vres-0.5);
  	glutDisplayFunc(draw_scene);
  	glutMainLoop();

	LiberarMemoria();

  	return 0;
}
